﻿using System;
using System.Runtime.InteropServices;

namespace BetterJoyForCemu {
	public class HIDapi {
#if LINUX
	    const string dll = "libhidapi.so";
#else
		const string dll = "hidapi.dll";
#endif

		public struct hid_device_info {
			[MarshalAs(UnmanagedType.LPStr)]
			public string path;
			public ushort vendor_id;
			public ushort product_id;
			[MarshalAs(UnmanagedType.LPWStr)]
			public string serial_number;
			public ushort release_number;
			[MarshalAs(UnmanagedType.LPWStr)]
			public string manufacturer_string;
			[MarshalAs(UnmanagedType.LPWStr)]
			public string product_string;
			public ushort usage_page;
			public ushort usage;
			public int interface_number;
			public IntPtr next;
		};

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_init();

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_exit();

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr hid_enumerate(ushort vendor_id, ushort product_id);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern void hid_free_enumeration(IntPtr phid_device_info);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr hid_open(ushort vendor_id, ushort product_id, [MarshalAs(UnmanagedType.LPWStr)] string serial_number);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern IntPtr hid_open_path([MarshalAs(UnmanagedType.LPStr)] string path);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_write(IntPtr device, byte[] data, UIntPtr length);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_read_timeout(IntPtr dev, byte[] data, UIntPtr length, int milliseconds);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_read(IntPtr device, byte[] data, UIntPtr length);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_set_nonblocking(IntPtr device, int nonblock);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_send_feature_report(IntPtr device, byte[] data, UIntPtr length);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_get_feature_report(IntPtr device, byte[] data, UIntPtr length);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern void hid_close(IntPtr device);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_get_manufacturer_string(IntPtr device, [MarshalAs(UnmanagedType.LPWStr)] string string_, UIntPtr maxlen);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_get_product_string(IntPtr device, [MarshalAs(UnmanagedType.LPWStr)] string string_, UIntPtr maxlen);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_get_serial_number_string(IntPtr device, [MarshalAs(UnmanagedType.LPWStr)] string string_, UIntPtr maxlen);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		public static extern int hid_get_indexed_string(IntPtr device, int string_index, [MarshalAs(UnmanagedType.LPWStr)] string string_, UIntPtr maxlen);

		[DllImport(dll, CallingConvention = CallingConvention.Cdecl)]
		[return: MarshalAs(UnmanagedType.LPWStr)]
		public static extern string hid_error(IntPtr device);

		static void PrintEnumeration(IntPtr phid_device_info) {
			if (!phid_device_info.Equals(IntPtr.Zero)) {
				hid_device_info hdev = (hid_device_info)Marshal.PtrToStructure(phid_device_info, typeof(hid_device_info));

				Console.WriteLine(string.Format("path:       {0}", hdev.path));
				Console.WriteLine(string.Format("vendor id:  {0:X}", hdev.vendor_id));
				Console.WriteLine(string.Format("product id: {0:X}", hdev.product_id));
				Console.WriteLine(string.Format("usage page: {0:X}", hdev.usage_page));
				Console.WriteLine(string.Format("usage:      {0:X}", hdev.usage));
				Console.WriteLine("");

				PrintEnumeration(hdev.next);
			}
		}

		static string _getDevicePath(IntPtr phid_device_info, ushort usagePage, ushort usage) {
			if (!phid_device_info.Equals(IntPtr.Zero)) {
				hid_device_info hdev = (hid_device_info)Marshal.PtrToStructure(phid_device_info, typeof(hid_device_info));
				if (usagePage == hdev.usage_page && usage == hdev.usage)
					return hdev.path;
				else
					return _getDevicePath(hdev.next, usagePage, usage);
			}
			return null;
		}

		public static string GetDevicePath(ushort vendorId, ushort productId, ushort usagePage, ushort usage) {
			return _getDevicePath(hid_enumerate(vendorId, productId), usagePage, usage);
		}
	}
}
